package network.protocol;
import java.util.Vector;

import game.Game;
import game.objects.GameObject;
import graphics.objects.GraphicsObject;
import network.dataFormats.GroToGO;
import network.dataFormats.NetworkPackage;
import physics.Body;
import physics.joints.Joint;
import physics.joints.TwoBodyJoint;
import tools.Help;
/**
 * Correctly handles adding and removing of the common types of objects. Each object will have one unique id assigned to them which is used to identify the objects. Whenever an object is added, its id will be saved in the "added..." array, when its removed it will be removed from this array and then added to the "removed..." array.
 * 
 * @author toni
 * 
 */
public class ObjectManager {
	private Game game;
	private ClientHandler clientHandler;
	private Vector<Integer> addedBodies;
	private Vector<Integer> removedBodies;
	private Vector<Integer> addedJoints;
	private Vector<Integer> removedJoints;
	private Vector<Integer> addedGraphicsObjects;
	private Vector<Integer> removedGraphicsObjects;
	private Vector<Integer> addedGameObjects;
	private Vector<Integer> removedGameObjects;
	public ObjectManager(ClientHandler c, Game g) {
		game = g;
		clientHandler = c;
		this.addedBodies = new Vector<Integer>();
		this.removedBodies = new Vector<Integer>();
		this.addedJoints = new Vector<Integer>();
		this.removedJoints = new Vector<Integer>();
		this.addedGraphicsObjects = new Vector<Integer>();
		this.removedGraphicsObjects = new Vector<Integer>();
		this.addedGameObjects = new Vector<Integer>();
		this.removedGameObjects = new Vector<Integer>();
	}
	public boolean addBody(Body b, NetworkPackage p) {
		if (removedBodies.contains(b.id))
			return false; // A remove body package has arrived before the body
							// has even been added. Don't process it.
		if (addedBodies.contains(b.id))
			return false;
		addedBodies.addElement(b.id);
		return true;
	}
	public boolean addJoint(Joint j, NetworkPackage p) {
		if (removedJoints.contains(j.id))
			return false; // A remove joint package has arrived before the joint
							// has even been added. Don't process it.
		if (addedJoints.contains(j.id))
			return false;
		// Check if the bodies that the joint depends on actually exist
		int id1 = j.b1Id;
		if (removedBodies.contains(id1)) {
			return false; // One of the body the joint depends on has been
							// deleted. Don't add it.
		}
		if (!addedBodies.contains(id1)) {
			clientHandler.pushBack(p); // The bodies in this joint have not yet
										// been added. Wait for them to arrive
			return false;
		}
		if (j instanceof TwoBodyJoint) {
			int id2 = ((TwoBodyJoint) j).b2Id;
			if (removedBodies.contains(id2)) {
				return false; // One of the body the joint depends on has been
								// deleted. Don't add it.
			}
			if (!addedBodies.contains(id2)) {
				clientHandler.pushBack(p);
				return false;
			}
		}
		addedJoints.addElement(j.id);
		return true;
	}
	public boolean addGraphicsObject(GraphicsObject gr, NetworkPackage p) {
		if (removedGraphicsObjects.contains(gr.id))
			return false; // A remove graphics object package has arrived before
							// the graphics object has even been added. Don't
							// process it.
		if (addedGraphicsObjects.contains(gr.id))
			return false;
		addedGraphicsObjects.addElement(gr.id);
		return true;
	}
	public boolean addGameObject(GameObject go, NetworkPackage p) {
		if (removedGameObjects.contains(go.id))
			return false; // A remove game object package has arrived before the
							// game object has even been added. Don't process
							// it.
		if (addedGameObjects.contains(go.id))
			return false;
		addedGameObjects.addElement(go.id);
		return true;
	}
	public boolean remBody(int id, NetworkPackage p) {
		if (removedBodies.contains(id))
			return false; // Object already removed
		removedBodies.add(id);
		if (!addedBodies.contains(id))
			return false; // Object not yet added
		addedBodies.removeElement(id);
		return true;
	}
	public boolean remJoint(int id, NetworkPackage p) {
		if (removedJoints.contains(id))
			return false; // Object already removed
		removedJoints.add(id);
		if (!addedJoints.contains(id))
			return false; // Object not yet added
		addedJoints.removeElement(id);
		return true;
	}
	public boolean remGraphicsObject(int id, NetworkPackage p) {
		if (removedGraphicsObjects.contains(id))
			return false; // Object already removed
		removedGraphicsObjects.add(id);
		if (!addedGraphicsObjects.contains(id))
			return false; // Object not yet added
		addedGraphicsObjects.removeElement(id);
		return true;
	}
	public boolean remGameObject(int id, NetworkPackage p) {
		if (removedGameObjects.contains(id))
			return false;
		removedGameObjects.addElement(id);
		if (!addedGameObjects.contains(id))
			return false;
		// Mark all the bodies, joints and grOs in this GO as removed
		GameObject go = game.getGOById(id);
		for (int i = 0; i < go.numBodies; i++) {
			removedBodies.addElement(i);
			addedBodies.removeElement(i);
		}
		for (int i = 0; i < go.numJoints; i++) {
			removedJoints.addElement(i);
			addedJoints.removeElement(i);
		}
		for (int i = 0; i < go.numGraphicsObjects; i++) {
			removedGraphicsObjects.addElement(i);
			addedGraphicsObjects.removeElement(i);
		}
		addedGameObjects.removeElement(id);
		return true;
	}
	public boolean addBodyToGameObject(int goId, int bodyId, NetworkPackage p) {
		if (addedGameObjects.contains(goId) && addedBodies.contains(bodyId)) {
			GameObject go = game.getGOById(goId);
			if (!game.engine.world.containsBodyId(bodyId)) {
				Help.p(bodyId);
				assert (addedBodies.contains(bodyId));
			}
			Body b = game.engine.world.getBodyById(bodyId);
			if (go.containsBody(b)) { // This GO has been initialized
										// completely. The body was not added
										// seperately, therefore it already
										// exists in the GO. don't process the
										// package.
				return false;
			}
			return true;
		}
		if (removedGameObjects.contains(goId) || removedBodies.contains(bodyId)) { // The
																					// GO
																					// or
																					// the
																					// body
																					// have
																					// been
																					// removed
																					// before
																					// this
																					// package
																					// arrived.
																					// Don't
																					// process
																					// it.
			return false;
		}
		// Otherwise: Wait for the ADD-package(s) to arrive.
		clientHandler.pushBack(p);
		return false;
	}
	public boolean addJointToGameObject(int goId, int jointId, NetworkPackage p) {
		if (addedGameObjects.contains(goId) && addedJoints.contains(jointId)) {
			GameObject go = game.getGOById(goId);
			Joint j = game.engine.world.getJointById(jointId);
			if (go.containsJoint(j)) { // This GO has been initialized
										// completely. The joint was not added
										// seperately, therefore it already
										// exists in the GO. don't process the
										// package.
				return false;
			}
			return true;
		}
		if (removedGameObjects.contains(goId) || removedJoints.contains(jointId)) { // The GO or the joint
																					// have been removed
																					// before this package
																					// arrived. Don't
																					// process it.
			return false;
		}
		// Otherwise: Wait for the ADD-package(s) to arrive.
		clientHandler.pushBack(p);
		return false;
	}
	public boolean addGraphicsObjectToGameObject(GroToGO g, NetworkPackage p) {
		if (addedGameObjects.contains(g.goId) && addedGraphicsObjects.contains(g.grId)) {
			GameObject go = game.getGOById(g.goId);
			GraphicsObject gr = game.engine.graphics.getGraphicsObjectById(g.grId);
			if (go.containsGraphicsObject(gr)) { // This GO has been initialized
													// completely. The body was
													// not added seperately,
													// therefore it already
													// exists in the GO. don't
													// process the package.
				return false;
			}
			// This graphicsObject is synced to something. Be sure that this
			// thing actually exists.
			switch (g.sync.type) {
				case BODY :
					return addedBodies.contains(g.sync.targetId);
				case JOINT :
					return addedJoints.contains(g.sync.targetId);
			}
			return true;
		}
		if (removedGameObjects.contains(g.goId) || removedGraphicsObjects.contains(g.grId)) { // The GO or the
																								// graphicsobject
																								// have been
																								// removed
																								// before this
																								// package
																								// arrived.
																								// Don't process
																								// it.
			return false;
		}
		// Otherwise: Wait for the ADD-package(s) to arrive.
		clientHandler.pushBack(p);
		return false;
	}
	public void setGame(Game g) {
		game = g;
		this.addedBodies = new Vector<Integer>();
		this.removedBodies = new Vector<Integer>();
		this.addedJoints = new Vector<Integer>();
		this.removedJoints = new Vector<Integer>();
		this.addedGraphicsObjects = new Vector<Integer>();
		this.removedGraphicsObjects = new Vector<Integer>();
		this.addedGameObjects = new Vector<Integer>();
		this.removedGameObjects = new Vector<Integer>();
		// Collect all bodies
		Body b;
		for (int i = 0; i < game.engine.world.numBodies; i++) {
			b = game.engine.world.getBody(i);
			addedBodies.add(b.id);
		}
		// Collect all joints
		Joint j;
		for (int i = 0; i < game.engine.world.numJoints; i++) {
			j = game.engine.world.getJoint(i);
			addedJoints.add(j.id);
		}
		// Collect all graphicsObjects
		GraphicsObject gr;
		for (int i = 0; i < game.engine.graphics.numGraphicsObjects; i++) {
			gr = game.engine.graphics.getGraphicsObject(i);
			addedGraphicsObjects.add(gr.id);
		}
		// Collect all gameObjects
		GameObject go;
		for (int i = 0; i < game.numGameObjects; i++) {
			go = game.getGameObject(i);
			addedGameObjects.add(go.id);
		}
	}
}
